package xyj.sparrow.scheduler.server.handler;

import io.netty.channel.Channel;

import xyj.sparrow.common.connect.SparrowConnect;
import xyj.sparrow.common.netty.bean.Request;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import xyj.sparrow.scheduler.server.ClientManager;
import xyj.sparrow.scheduler.server.connect.ConnectManager;
import xyj.sparrow.scheduler.util.ApplicationContextUtil;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName IdleServerHandler
 * @Description TODO
 * @Author Jim
 * @Date 2021/1/19 16:58
 * @Version 1.0.0
 **/
@Slf4j
public class IdleServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 断开阈值
     **/
    private static final int CLOSE_THRESHOLD = 3;
    private static final Object PING = "PING";
    private static final Object PONG = "PONG";
    /**
     * 连续读空闲 计数
     **/
    private final AtomicInteger times = new AtomicInteger(0);
    private final static AtomicInteger get = new AtomicInteger(0);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        times.set(0);
        if (!Request.class.isAssignableFrom(msg.getClass())) {
            log.info("server receive idle {}: {}", get.incrementAndGet(), msg);
            if (PING.equals(msg)) {
                sendPong(ctx);
            }
        } else {
            log.info(msg.getClass().getName());
        }
        ctx.fireChannelRead(msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        log.info("客户端连接成功");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        log.info("客户端掉线了");
        SparrowConnect connect = new SparrowConnect();
        connect.setChannel(ctx.channel());
        ApplicationContextUtil.getBean(ConnectManager.class).removeConnect(connect);
        ctx.channel().close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.channel().close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (!IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
            return;
        }
        IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
        if (!idleStateEvent.state().equals(IdleState.READER_IDLE)) {
            return;
        }
        int idleTimes = times.addAndGet(1);
        if (idleTimes >= CLOSE_THRESHOLD) {
            log.info("三次连续读空闲，将断开客户端");
            ctx.channel().close();
        } else {
            log.info("第" + idleTimes + "次读空闲");
            //发送ping
            sendPing(ctx);
        }
    }

    private void sendPong(ChannelHandlerContext ctx) {
        log.info("server send idle : " + PONG);
        ctx.channel().writeAndFlush(PONG);
    }

    private void sendPing(ChannelHandlerContext ctx) {
        log.info("server send idle : " + PING);
        ctx.channel().writeAndFlush(PING);
    }

}
